package com.example.demo.config;

import com.example.common.entity.UserInfo;
import com.example.common.tools.AjaxResult;
import com.example.common.tools.AppVariable;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import com.example.demo.entity.ChatMessageInfo;
import com.example.demo.entity.FollowInfo;
import com.example.demo.service.FollowService;
import com.example.demo.service.UserService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class WebSocketAPI extends TextWebSocketHandler {

    @Autowired
    private UserService userService;

    @Autowired
    private FollowService followService;

    private ObjectMapper objectMapper = new ObjectMapper();

    //用来存储每一个客户端对应的 websocketSession 信息
    public static ConcurrentHashMap<Integer, WebSocketSession> onlineUserManager = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //用户上线，加入到 onlineUserManager，推送上线消息给所有客户端

        //1.获取当前用户信息(是谁在建立连接)
        // 这里能够获取到 session 信息，依靠的是注册 websocket 的时候，
        // 加上的 addInterceptors(new HttpSessionHandshakeInterceptor()) 就是把 HttpSession 中的 Attribute 都拿给了 WebSocketSession 中
        //注意：此处的 userinfo 有可能为空，因为用户有可能直接通过 url 访问私信页面，此时 userinfo 就为 null，因此就需要 try catch
        try {
            UserInfo userInfo = (UserInfo) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
            //2.先判断当前用户是否正在登录，如果是就不能进行后面的逻辑
            WebSocketSession socketSession = onlineUserManager.get(userInfo.getId());
            if(socketSession != null) {
                //当前用户已登录,就要告诉客户端重复登录了
                session.sendMessage(new TextMessage(objectMapper.writeValueAsBytes(AjaxResult.fail(403, "当前用户正在登录，禁止多开!"))));
                session.close();
                return;
            }
            //3.拿到身份信息之后就可以把当前登录用户设置成在线状态了
            onlineUserManager.put(userInfo.getId(), session);
            System.out.println("用户:" + userInfo.getUsername() + "进入聊天室");
            //4.将当前在线的用户名推送给所有的客户端
            //4.1、获取当前用户的好友(相互关注)中所有在线的用户
            //注意：这里的 init 表示告诉客户端这是在初始化好友列表
            List<ChatMessageInfo> friends = getCurUserFriend(session);
            AjaxResult ajaxResult = AjaxResult.success("init", friends);
            //把好友列表消息推送给当前用户
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(ajaxResult)));
            //将当前用户上线消息推送给所有他的好友(通过 id)
            for(ChatMessageInfo friend : friends) {
                WebSocketSession friendSession = onlineUserManager.get(friend.getId());
                if(friendSession != null) {
                    friendSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.success("online", userInfo.getId()))));
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
            //说明此时的用户未登录
            //先通过 ObjectMapper 包装成一个 JSON 字符串
            //然后用 TextMessage 进行包装，表示是一个 文本格式的 websocket 数据包
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.fail(403, "您尚未登录!"))));
        }
    }

    /**
     * 获取所有在线用户信息
     * @return
     */
    private List<ChatMessageInfo> getCurUserFriend(WebSocketSession session) throws IOException {
        //1.筛选出当前用户相互关注的用户
        //1.1获取当前用户所有关注的用户列表
        List<ChatMessageInfo> resUserinfoList = new ArrayList<>();
        try {
            UserInfo curUserInfo = (UserInfo) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
            List<FollowInfo> followInfos = followService.getConcernListByUid(curUserInfo.getId());
            //好友列表(相互关注的用户列表)
            for(FollowInfo followInfo : followInfos) {
                //1.2获取被关注的人的 id 列表，检测是否出现了关注当前用户的人
                List<FollowInfo> otherList =  followService.getConcernListByUid(followInfo.getFollow_id());
                for(FollowInfo otherInfo : otherList) {

                    //1.3检测被关注的人是否也关注了自己
                    if(followInfo.getUid().equals(otherInfo.getFollow_id())) {
                        //1.4相互关注的用户
                        UserInfo friendInfo = userService.getUserById(otherInfo.getUid());
                        ChatMessageInfo chatMessageInfo = new ChatMessageInfo();
                        chatMessageInfo.setId(friendInfo.getId());
                        chatMessageInfo.setNickname(friendInfo.getNickname());
                        chatMessageInfo.setPhoto(friendInfo.getPhoto());
                        //设置在线信息(在 onlineUserManager 中说明在线)
                        if(onlineUserManager.get(friendInfo.getId()) != null) {
                            chatMessageInfo.setOnline("在线");
                        } else {
                            chatMessageInfo.setOnline("离线");
                        }
                        resUserinfoList.add(chatMessageInfo);
                    }
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.fail(403, "您尚未登录!"))));
        }
        return resUserinfoList;
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //实现处理发送消息操作
        UserInfo userInfo = (UserInfo) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
        //获取客户端发送过来的数据(数据载荷)
        String payload = message.getPayload();
        //当前这个数据载荷是一个 JSON 格式的字符串，就需要解析成 Java 对象
        AjaxResult request = objectMapper.readValue(payload, AjaxResult.class);
        //对方的 id
        Integer otherId = request.getCode();
        //要发送给对方的消息
        String msg = request.getMsg();
        //将消息发送给对方
        WebSocketSession otherSession = onlineUserManager.get(otherId);
        if(otherSession == null) {
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.fail(403,"对方不在线!"))));
            return;
        }
        otherSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.success(userInfo.getId(),"msg",  msg))));
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        try {
            //用户下线，从 onlineUserManager 中删除
            UserInfo userInfo = (UserInfo) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
            onlineUserManager.remove(userInfo.getId());
            //通知该用户的所有好友，当前用户已下线
            List<ChatMessageInfo> friends = getCurUserFriend(session);
            //将当前用户下线消息推送给所有他的好友(通过 id)
            for(ChatMessageInfo friend : friends) {
                WebSocketSession friendSession = onlineUserManager.get(friend.getId());
                if(friendSession != null) {
                    friendSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.success("offline", userInfo.getId()))));
                }
            }
        } catch(NullPointerException e) {
            e.printStackTrace();
            //说明此时的用户未登录
            //先通过 ObjectMapper 包装成一个 JSON 字符串
            //然后用 TextMessage 进行包装，表示是一个 文本格式的 websocket 数据包
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.fail(403, "您尚未登录!"))));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        try {
            //用户下线，从 onlineUserManager 中删除
            UserInfo userInfo = (UserInfo) session.getAttributes().get(AppVariable.USER_SESSION_KEY);
            onlineUserManager.remove(userInfo.getId());
            //通知该用户的所有好友，当前用户已下线
            List<ChatMessageInfo> friends = getCurUserFriend(session);
            //将当前用户下线消息推送给所有他的好友(通过 id)
            for(ChatMessageInfo friend : friends) {
                WebSocketSession friendSession = onlineUserManager.get(friend.getId());
                if(friendSession != null) {
                    friendSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.success("offline", userInfo.getId()))));
                }
            }
        } catch (NullPointerException e) {
            e.printStackTrace();
            //说明此时的用户未登录
            //先通过 ObjectMapper 包装成一个 JSON 字符串
            //然后用 TextMessage 进行包装，表示是一个 文本格式的 websocket 数据包
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(AjaxResult.fail(403, "您尚未登录!"))));
        }
    }

}
